package p19_动态通知调用;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.util.List;

//简单介绍下动态调用底层
public class _2_动态通知原理 extends AnnotationAwareAspectJAutoProxyCreator {
    public static void main(String[] args) throws Throwable {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
        //AnnotationAwareAspectJAutoProxyCreator构造器是受保护的，这里使用其子类，也就是本类
        context.registerBean(_2_动态通知原理.class);
        context.refresh();

        _2_动态通知原理 creator = context.getBean(_2_动态通知原理.class);
        //获取所有适合Config.Target这个bean的低级切面集合
        List<Advisor> advisors = creator.findEligibleAdvisors(Config.Target.class, "target");

        //使用ProxyFactory，根据低级切面集合和目标对象，生成代理对象(底层使用了jdk代理或者cglib)
        Config.Target target = new Config.Target();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisors(advisors);
        Config.Target proxy = (Config.Target)factory.getProxy();
        //将低级切面统一转为环绕通知
        List<Object> list = factory.getInterceptorsAndDynamicInterceptionAdvice(Config.Target.class.getMethod("foo", int.class), Config.Target.class);
        /**
         * 得到所有环绕通知
         *  1.org.springframework.aop.interceptor.ExposeInvocationInterceptor
         *  2.org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
         *  3.org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher
         * 前两个是老熟人了，第三个我们没见过，而且它也没实现MethodInterceptor，说明不是环绕通知
         * 第三个通知就是我们定义的动态通知(它组合了环绕通知和切点对象，所以它能获取到目标方法的参数)
         * 第三个虽然没有实现MethodInterceptor，但组合了MethodInterceptor，所以也算是环绕
         */
        for (Object o : list) {
            System.out.println(o);
        }

        //将上面得到的所以通知和目标类封装成调用链，执行
        System.out.println("=====================>>>>>>>>");
        ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, target.getClass().getMethod("foo", int.class), new Object[]{1}, Config.Target.class, list){};
        invocation.proceed();
        //也可以直接执行代理对象，它里面就间接调用了这个调用链
        proxy.foo(22);

        /**
         * 总结：
         * 带参数绑定的通知，它为了进行参数的匹配与绑定，所以这种通知需要额外持有切点对象
         * 比较复杂，性能低
         */
    }
}
